iT邦幫忙

2021 iThome 鐵人賽

DAY 16
1
Modern Web

在JS的世界碰碰撞撞乒乒乓乓!30天一起玩Matter.js!系列 第 16

Day16. 老鼠,老虎傻傻分不清楚?- Mouse(上)

  • 分享至 

  • xImage
  •  

昨天最後說了,我們要幫彈珠台最後加上滑鼠的操控效果,所以今天讀的文件會是 Mouse 這個模組。

Mouse 模組相對在實作中較少用到,主要是互動的一些方式都被定義在另一個模組:MouseConstraint,那個模組我們會在明天來看。

今天的Demo
今天的Demo原始碼
https://ithelp.ithome.com.tw/upload/images/20211001/20142057pO9DDhttXs.png

為什麼 Mouse 模組不容易用到,還要抽一篇來講它呢?

一個是雖然他的函式少用,但是相關屬性如果有要做 mouse 相關操作最好要有所了解,一個是筆者想一樣在這篇走走原始碼的部分,走走原始碼除了更能了解詳細內容,也可能可以從原始碼中看到一些作者設計的想法,有機會的話看看這種公用函式庫原始碼其實是很不錯的事。

Mouse 在 Matter.js 的世界中其實是一種物件型別,我們來看一下 Mouse 這個模組總共有多少種方法:

  • Matter.Mouse.create(element)
  • Matter.Mouse.setElement(mouse, element)
  • Matter.Mouse.setOffset(mouse, offset)
  • Matter.Mouse.setScale(mouse, scale)
  • Matter.Mouse.clearSourceEvents(mouse)

先來看到 create 方法的原始碼,裡面寫著如何建構一個 mouse物件。

可以看到 mouse 創建本身會相依於一個 HTMLelement,其實就是定義這個宣告的 mouse 物件的作用範圍。可以僅限於 canvas上,若不輸入的話會 default 拿 body 當 element的對象。

var mouse = {};
if (!element) {
    Common.log('Mouse.create: element was undefined, defaulting to document.body', 'warn');
}
mouse.element = element || document.body;

再來有一段為 mouse 建立基本屬性:

mouse.element = element || document.body;
mouse.absolute = { x: 0, y: 0 };
mouse.position = { x: 0, y: 0 };
mouse.mousedownPosition = { x: 0, y: 0 };
mouse.mouseupPosition = { x: 0, y: 0 };
mouse.offset = { x: 0, y: 0 };
mouse.scale = { x: 1, y: 1 };
mouse.wheelDelta = 0;
mouse.button = -1;
mouse.pixelRatio = parseInt(mouse.element.getAttribute('data-pixel-ratio'), 10) || 1;

這邊在 API 文件中並沒有一一說明各個屬性的套用地方,我們也就先不在這段深究,我們會再後面綁定的 event 中看到一些設置或使用,直觀看到的會是像是相對位置、絕對位置,點擊位置,偏移、縮放等等屬性。

創建的最後是幫 mouse 綁上特定事件的監聽:

mouse.mousemove = function(event) { 
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        mouse.button = 0;
        event.preventDefault();
    }

    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.sourceEvents.mousemove = event;
};
mouse.mousedown = function(event) {
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        mouse.button = 0;
        event.preventDefault();
    } else {
        mouse.button = event.button;
    }

    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.mousedownPosition.x = mouse.position.x;
    mouse.mousedownPosition.y = mouse.position.y;
    mouse.sourceEvents.mousedown = event;
};

mouse.mouseup = function(event) {
    var position = Mouse._getRelativeMousePosition(event, mouse.element, mouse.pixelRatio),
        touches = event.changedTouches;

    if (touches) {
        event.preventDefault();
    }
    
    mouse.button = -1;
    mouse.absolute.x = position.x;
    mouse.absolute.y = position.y;
    mouse.position.x = mouse.absolute.x * mouse.scale.x + mouse.offset.x;
    mouse.position.y = mouse.absolute.y * mouse.scale.y + mouse.offset.y;
    mouse.mouseupPosition.x = mouse.position.x;
    mouse.mouseupPosition.y = mouse.position.y;
    mouse.sourceEvents.mouseup = event;
};

mouse.mousewheel = function(event) {
    mouse.wheelDelta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail));
    event.preventDefault();
};

總共有四個事件

  • MouseMove
  • MouseDown
  • MouseUp
  • MouseWheel

這邊從名字看應該能看出對應行為:滑鼠移動,滑鼠點擊、滑鼠點擊後放開、滑鼠滾輪。

滑鼠移動的事件就是單純的紀錄位置,touch有一個isTouchChange的邏輯可以參考MDN文件,等等我們會看到,其實這邊自訂的事件都會跟瀏覽器原生的一些事件是互有關係的,從這邊建構丟入 event 就可以看出一些端倪。

滑鼠點擊的兩個事件主要是會辨認點擊當下的滑鼠位置與透過 preventDefault 來避免預設的事件相衝。 button 看起來像是用來判斷是否持續維持點擊狀態,如果持續點擊則為 0,如果放開則為 -1。

滑鼠滾輪的事件看起來會像是去抓取滾輪的差量,也就是這次滾動事件的滾動差距量。

這幾個事件的目的都是在原生瀏覽器滑鼠事件觸發的時候,來改動 mouse 這個物件本身的屬性。

最後會做一個 setElement,剛好講到我們的第二個函式。

Mouse.setElement(mouse, mouse.element);

第二個函式會是把 mouse 物件指派到某個 HTMLelement上,同時這個 element 也會等同自身的 element屬性。

Mouse.setElement = function(mouse, element) {
    mouse.element = element;

    element.addEventListener('mousemove', mouse.mousemove);
    element.addEventListener('mousedown', mouse.mousedown);
    element.addEventListener('mouseup', mouse.mouseup);
    
    element.addEventListener('mousewheel', mouse.mousewheel);
    element.addEventListener('DOMMouseScroll', mouse.mousewheel);

    element.addEventListener('touchmove', mouse.mousemove);
    element.addEventListener('touchstart', mouse.mousedown);
    element.addEventListener('touchend', mouse.mouseup);
};

在 setElement 這個方法中,我們可以明確地看到其實就是把幾個跟滑鼠相關的 event 綁定到瀏覽起本來定義的幾個滑鼠行為,同時將那些滑鼠行為對應到我們上便提到的四個行為中。

也就是只監聽指定 HTMLelement 上的滑鼠行為,如果你定義在 canvas 上,可以避免點擊頁面上除了 canvas 的地方也觸發。

第三跟第四個的原始碼筆者就不轉貼了,可以直接參考原檔,基本上就是對 mouse 內的屬性作一個對外的 setter,沒有特別的邏輯。

第五個 clearSourceEvents 會是把這邊 mouse 模組定義的方法都拿掉,也就是不依賴原生的事件來更新 mouse 本身的屬性。

Mouse.clearSourceEvents = function(mouse) {
    mouse.sourceEvents.mousemove = null;
    mouse.sourceEvents.mousedown = null;
    mouse.sourceEvents.mouseup = null;
    mouse.sourceEvents.mousewheel = null;
    mouse.wheelDelta = 0;
};

差不多到這裡就完整走完我們整個 mouse 物件相關的原始碼了,今天的 Demo 筆者在 canvas 上綁了點擊就會記錄當下 mosue 的狀態在 console.log,大家可以試試看在不同地方點擊然後觀察屬性的變化。

另外也讓 frameUpdate 事件裡當 mouseObject = 0 的時候會寫 log,這會是實作上一個偵測點擊後依滑鼠位置來做對應處理,如施力/位移等方法的實作思路。

我們今天的內容到此告一段落,明天會帶大家來看怎麼利用 mouse 物件來和我們的畫面作互動。


上一篇
Day15. 一起動手做彈珠台!(3)
下一篇
Day17. 老鼠,老虎傻傻分不清楚?- Mouse(下)
系列文
在JS的世界碰碰撞撞乒乒乓乓!30天一起玩Matter.js!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言